module.exports = Path;

var SubPath = require('./subpath')
  , ClipperLib = require('./clipper')
  , utils = require('./utils')
  , Point = require('./math/point')
  , Matrix = require('./math/matrix')
  , conf = require('./conf')

const RESOLUTION = 1000; // 精度, 保留到小数点第三位


function Path() {
  this.subPaths = [];
}

Path.actions = SubPath.actions;

Path.prototype = {
  clone: function () {
    var copy = new Path();
    copy.subPaths = this.subPaths.slice(0);
    return copy;
  }
  , apend: function (path) {
    if (!path) return;
    for (var i = 0, l = path.subPaths.length; i < l; i++) {
      this.subPaths.push(path.subPaths[i].clone());
    }
  }
  , moveTo: function (x, y) {
    var subPath = new SubPath();
    subPath.moveTo(x, y);
    this.subPaths.push(subPath);
    this.current = subPath; // 指向最后一个子路径
  }
  , _ensure: function (x, y) {
    if (this.subPaths.length === 0) {
      this.moveTo(x, y);
    }
  }

  , close: function () {
    if (!this.current) return false;
    this.current.close();
  }

  /*
   * Pass all curves straight through
   * */
  , lineTo: function (x, y) {
    this._ensure(x, y);
    this.current.lineTo.apply(this.current, arguments);
  }
  , arc: function (x, y, rad,
    astart, aend, ccw) {
    this.ellipse(x, y, rad, rad, astart, aend, ccw);
  }
  , ellipse: function (x, y, xrad, yrad,
    astart, aend, ccw) {

    var points = utils.arcToPoints(x, y,
      astart,
      aend,
      xrad);

    // this._ensure(points.start.x, points.start.y);

    if (!this.current || !utils.samePos(this.current.lastPoint(), points.start)) {
      this.lineTo(points.start.x, points.start.y);
    }

    this.current.ellipse.apply(this.current, arguments);
  }
  , quadraticCurveTo: function () {
    this.current.quadraticCurveTo.apply(this.current, arguments);
  }
  , bezierCurveTo: function () {
    this.current.bezierCurveTo.apply(this.current, arguments);
  }
  , rect: function (x, y, w, h) {
    this.moveTo(x, y);
    this.lineTo(x + w, y);
    this.lineTo(x + w, y + h);
    this.lineTo(x, y + h);
    this.lineTo(x, y);
  }
  // 将当前对象转化为多边形表示，（用于clipper）
  , toPolys: function (scale, divisions) {
    if (!scale) throw 'NO SCALE!';

    return this.subPaths.map(function (subPath) {
      return subPath.toPoly(scale, divisions);
    });
  }
  // 从 clipper 类型转换为当前
  , fromPolys: function (polygons, scale, isClosed = true) {
    if (!scale) throw 'NO SCALE!';

    this.subPaths = [];

    for (var i = 0, l = polygons.length; i < l; ++i) {
      var subPath = new SubPath();
      subPath.fromPoly(polygons[i], scale, isClosed);
      this.subPaths.push(subPath);
      this.current = subPath;
    }

    return this;
  }
  // 路径裁剪
  // clipRegion：裁剪区域
  // clipType：裁剪类型，0：正常裁剪，1：反向裁剪，2：相交裁剪
  , clip: function (clipRegion, clipType, divisions) {
    if (!clipRegion) return this;

    clipType = clipType || 0;

    // this.close();
    // clipRegion.close();

    var subjPolys = this.toPolys(RESOLUTION, divisions);
    var clipPolys = clipRegion.toPolys(RESOLUTION);

    // Clean both
    // var subjPolys = ClipperLib.Clipper.CleanPolygons(subjPolys, 1);
    // var clipPolys = ClipperLib.Clipper.CleanPolygons(clipPolys, 1);

    // var subjPolys = ClipperLib.Clipper.SimplifyPolygons(subjPolys, ClipperLib.PolyFillType.pftNonZero);

    // var clipPolys = ClipperLib.Clipper.SimplifyPolygons(clipPolys, ClipperLib.PolyFillType.pftNonZero);

    var cpr = new ClipperLib.Clipper();
    // cpr.PreserveCollinear = true;
    // cpr.ReverseSolution = true;

    cpr.AddPaths(subjPolys, ClipperLib.PolyType.ptSubject, true);
    cpr.AddPaths(clipPolys, ClipperLib.PolyType.ptClip, true);

    var clipped = [];
    cpr.Execute(clipType, clipped);

    var path = new Path();
    path.fromPolys(clipped, RESOLUTION);
    return path;
  }

  , translate: function (x, y) {
    var result = new Path();
    this.subPaths.forEach(function (subPath) {
      var pts = subPath.getPoints();
      result.moveTo(pts[0].x + x, pts[0].y + y);
      pts.slice(1).forEach(function (p) {
        // p.x += x;
        // p.y += y;
        result.lineTo(p.x + x, p.y + y);
      });
    });
    return result;
  }

  , transform: function (matrix) {
    var result = new Path();
    this.subPaths.forEach(function (subPath) {
      subPath.transform(matrix);
    });
    this.current = this.subPaths[this.subPaths.length - 1];
    return result;
  }

  // TODO：检查各个加减操作对不对， 裁剪到指定矩形区域
  , clipToBounds: function (bounds) {
    var result = new Path();
    var p0 = new Point(0, 0, 0);
    var p0u = p0.clone();
    var p1u;

    this.subPaths.forEach(function (subPath) {
      var pts = subPath.getPoints();

      pts.forEach(function (p1, i) {
        p1 = p1.clone();
        p1u = p1.clone();

        // if(p1.y < bounds.top && p0.y < bounds.top) {
        //   return;
        // }
        // if(p1.x > bounds.right && p0.x > bounds.right) {
        //   return;
        // }

        if (p1.y < bounds.top) {
          var m = (p1.x - p0.x) / (p1.y - p0.y);
          p1.x += (m * (bounds.top - p1.y)) || 0;
          p1.y = bounds.top;


        }
        else if (p0u.y < bounds.top) {
          var m = (p1.x - p0u.x) / (p1.y - p0u.y);
          var x = (m * (bounds.top - p1.y)) || 0;

          result.moveTo(p1.x + x, bounds.top);
        }

        // if(p1.x < bounds.left) {
        //   var m = (p1.y - p0.y) / (p1.x - p0.x);
        //   p1.y += m * (bounds.left - p1.x);
        //   p1.x = bounds.left;
        // }
        // else if(p0u.x < bounds.left) {
        //   var m = (p1.y - p0u.y) / (p1.x - p0u.x);
        //   var y = m * (bounds.left - p1.x);
        //   // result.moveTo(bounds.left, bounds.top);
        // }

        if (p1.x > bounds.right) {
          var m = (p1.y - p0.y) / (p1.x - p0.x);
          p1.y += m * (bounds.right - p1.x);
          p1.x = bounds.right;

        }
        else if (p0u.x > bounds.right) {

          var m = (p1.y - p0u.y) / (p1.x - p0u.x);
          var y = m * (bounds.right - p1.x);

          // result.moveTo(bounds.right, p1.y-y);
        }


        if (i === 0)
          result.moveTo(p1.x, p1.y);
        else
          result.lineTo(p1.x, p1.y);

        p0 = p1;
        p0u = p1u;
      });
    });

    return result;
  }

  , simplify: function (fillRule, divisions) {

    // Special case for single ellipse
    // just change the radius.
    // if(this.is('ellipse')) {
    //     var result = new Path();
    //     var args = this.subPaths[0].actions[1].args;

    //     result.ellipse(
    //       args[0],
    //       args[1],
    //       args[2],
    //       args[3],
    //       args[4],
    //       args[5],
    //       args[6]
    //     );

    //     return result;
    // }

    var polys = this.toPolys(RESOLUTION, divisions);
    // 非零环绕数规则
    // 如果一个点被多边形边界逆时针包围，则被认为是内部点，否则为外部点。
    var type = ClipperLib.PolyFillType.pftEvenOdd;

    if (fillRule === 'evenodd') {
      // 奇偶环绕数规则
      // 如果一个点在多边形内部被边界包围的次数为奇数，则被认为是内部点，否则为外部点。
      type = ClipperLib.PolyFillType.pftEvenOdd;
    } else if (fillRule === 'nonzero') {
      type = ClipperLib.PolyFillType.pftNonZero;
    } else if (fillRule === 'positive') {
      type = ClipperLib.PolyFillType.pftPositive;
    } else if (fillRule === 'negative') {
      type = ClipperLib.PolyFillType.pftNegative;
    } else {
      throw 'Invalid fill rule';
    }

    polys = ClipperLib.Clipper.SimplifyPolygons(polys, type);

    var result = new Path();
    result.fromPolys(polys, RESOLUTION);

    return result;
  }

  , is: function (action) {
    if (this.subPaths.length == 1
      && this.subPaths[0].actions.length == 2
      && this.subPaths[0].actions[1].action === action) {
      return true;
    }

    return false;
  }

  // delta ： 路径偏移量
  , offset: function (delta, divisions) {
    // 偏移量为 0，直接返回
    if (delta === 0) {
      return this;
    }

    // 特殊情况：单个椭圆形，通过画不同半径的椭圆实现回形填充
    if (this.is('ellipse')) {
      var result = new Path();
      var args = this.subPaths[0].actions[1].args;

      if (args[2] + delta < 0) // 通过 delta 变换后的半径小于零，无法生成椭圆
        return false;

      result.ellipse(
        args[0],
        args[1],
        args[2] + delta,
        args[3] + delta,
        args[4],
        args[5],
        args[6]
      );

      return result;
    }

    var cleandelta = 0.1;

    var polygons = this.toPolys(RESOLUTION, divisions); // 将当前对象转化为多边形表示

    // offset
    var miterLimit = 1000 * RESOLUTION;

    var co = new ClipperLib.ClipperOffset(); // 用来处理多边形偏移
    // co.PreserveCollinear = true;
    // co.ReverseSolution = true;

    co.AddPaths(polygons,
      ClipperLib.JoinType.jtMiter, // 
      ClipperLib.EndType.etClosedPolygon);
    
    // co.AddPaths(polygons,
    //   ClipperLib.JoinType.jtRound, // 
    //   ClipperLib.EndType.etClosedPolygon);

    var solution = [];

    try {
      co.Execute(solution, delta * RESOLUTION); // 执行偏移变换
    }
    catch (err) {
      return false;
    }

    if (!solution || solution.length === 0
      || solution[0].length === 0) return false; // 变换结果无效（空或没有多边形）

    var result = new Path();
    result.fromPolys(solution, RESOLUTION);

    // result.close(); // 调用 close 方法闭合路径
    // 没必要闭合路径，不然会有奇怪的线段
    return result; //返回新的路径对象
  }

  // 用于检查从点 p1 到点 p2 的直线段是否越过了给定的边界 bounds
  , crosses: function (bounds, p1, p2) {
    if (bounds === null) {
      return true; // 边界为空，默认越界
    }
    if (p1.X === p2.X && p1.Y === p2.Y) {
      return false; // 两点重合，不越界
    }

    let clipper = new ClipperLib.Clipper();
    // 将直线段 [p1, p2] 添加为主要路径
    clipper.AddPath([p1, p2], ClipperLib.PolyType.ptSubject, false);
    // 将 bounds 中的多边形作为剪切路径添加
    clipper.AddPaths(bounds, ClipperLib.PolyType.ptClip, true);
    // 执行剪切操作，计算主要路径和剪切路径的交集，并将结果存储在 result 中
    let result = new ClipperLib.PolyTree();
    clipper.Execute(ClipperLib.ClipType.ctIntersection, result, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd);

    if (result.ChildCount() === 1) { // 表示有一个交集区域被找到
      let child = result.Childs()[0];
      let points = child.Contour();
      if (points.length === 2) { // 检查这个区域是否由两个点构成
        // 这两个点与 p1 和 p2 的坐标相匹配，表示直线段并没有越界
        if (points[0].X === p1.X && points[1].X === p2.X && points[0].Y === p1.Y && points[1].Y === p2.Y)
          return false;
        if (points[0].X === p2.X && points[1].X === p1.X && points[0].Y === p2.Y && points[1].Y === p1.Y)
          return false;
      }
    }
    return true;
  }

  , pathIsClosed(clipperPath) {
    return (
      clipperPath.length >= 2 &&
      clipperPath[0].X === clipperPath[clipperPath.length - 1].X &&
      clipperPath[0].Y === clipperPath[clipperPath.length - 1].Y);
  }
  // 闭合所有路径
  , closeClipperPaths: function (paths) {
    for (let path of paths)
      path.push(path[0]);
  }

  , ramp: function (depth) {
  }

  , addPath: function (path2) {
    this.subPaths = this.subPaths.concat(path2.subPaths);
  }

  // 估算路径能够进行的最大偏移量
  // divisions： 偏移路径的分段数
  , estimateMaxOffset: function (divisions) {
    var bounds = this.getBounds();
    var width = Math.abs(bounds.right - bounds.left)
    var height = Math.abs(bounds.top - bounds.bottom)
    // 将结果限定在 [gt, lt]  // greater than and less than
    var lt = Math.min(width, height) / 2;
    var gt = 0;
    // 二分法进行最大偏移量的估算
    for (var i = 0; i < 5; ++i) {
      var test = gt + (lt - gt) / 2;
      // 反复进行偏移，直到路径变为空
      var offset = this.offset(-test, divisions);

      if (offset) {
        gt = test
      }
      else {
        lt = test;
      }
    }

    return { lt: lt, gt: gt }; // 最大偏移量和最小偏移量
  }

  // 环形填充算法
  , fillPathOffset: function (diameter, divisions) {
    var result = new Path();
    // 填充步进值。
    var overlap = Math.sin(Math.PI / 4);

    // this.subPaths.forEach(function(sp) {
    // var path = sp.toPath();
    var path = this;

    // -------------------------- 旧算法 均匀填充设置
    // var max = path.estimateMaxOffset(5).lt;
    // max -= diameter / 2;

    // // 根据直径和重叠来生成偏移路径并添加到 result 中
    // // 循环从 -max 开始，每次增加 diameter * overlap，直到 -diameter/2:
    // for (var i = -max; i < -diameter / 2; i += diameter * overlap) {
    //   var offsetPath = path.offset(i, divisions); // 生成偏移路径
    //   if (!offsetPath) break; // 路径为空，退出循环

    //   offsetPath = offsetPath.reverse();
    //   // offsetPath = offsetPath.close(); // 闭合路径
    //   result.addPath(offsetPath);
    // }

    // // 最后一段离边框很近
    // var finish = path.offset(-diameter / 2, divisions); // 生成最后一段路径
    // if (finish) { result.addPath(finish.reverse()); }

    // ----------------------------------

    let bounds = this.getBounds();
    let width = Math.abs(bounds.right - bounds.left)
    let height = Math.abs(bounds.top - bounds.bottom)
    // 将结果限定在 [gt, lt]  // greater than and less than
    let lt = Math.min(width, height) / 2;
    // 从外向里填充
    for (let i = -diameter / 2; i > -lt; i -= diameter) {
      let offsetPath = path.offset(i, divisions); // 生成偏移路径
      if (!offsetPath) break; // 路径为空，退出循环
      result.addPath(offsetPath);
    }

    // ------------- 
    return result;
  }
  // 连接路径的末尾，使相邻路径段的末端在距离内能够连接起来
  // diameter： 连接的最大距离
  , connectEnds: function (diameter) {
    for (var i = this.subPaths.length - 1; i > 0; --i) {
      var sp1 = this.subPaths[i - 1];
      var sp2 = this.subPaths[i]; // 

      var p1 = sp1.lastPoint(); // 路径段 sp1 的末尾点 p1
      var nearest = sp2.nearestPoint(p1);
      var p2 = nearest.point; // 下一个路径段 sp2 中离 p1 最近的点 p2

      if (nearest.distance < diameter * 2) {
        sp2 = sp2.shift(nearest.i); // 将 sp2 从 nearest.i 处进行调整。
        sp1.lineTo(p2.x, p2.y); // 将 sp1 的末尾连接到 p2
        sp2.actions[0].action = Path.actions.LINE_TO; // 将 sp2 的动作转换为直线连接动作
        sp1.actions = sp1.actions.concat(sp2.actions); // 其动作合并到 sp1 的动作中
        this.subPaths.splice(i, 1); // 从 subPaths 中移除被连接的 sp2
      }
    }

    return this;
  }

  // 最短路径算法
  , mergePaths: function () {
    for (var i = this.subPaths.length - 1; i > 0; --i) {
      var sp1 = this.subPaths[i - 1];
      var sp2 = this.subPaths[i]; // 

      var p1 = sp1.lastPoint(); // 路径段 sp1 的末尾点 p1
      var nearest = sp2.nearestPoint(p1);
      var p2 = nearest.point; // 下一个路径段 sp2 中离 p1 最近的点 p2

      if (nearest.distance < diameter * 2) {
        sp2 = sp2.shift(nearest.i); // 将 sp2 从 nearest.i 处进行调整。
        sp1.lineTo(p2.x, p2.y); // 将 sp1 的末尾连接到 p2
        sp2.actions[0].action = Path.actions.LINE_TO; // 将 sp2 的动作转换为直线连接动作
        sp1.actions = sp1.actions.concat(sp2.actions); // 其动作合并到 sp1 的动作中
        this.subPaths.splice(i, 1); // 从 subPaths 中移除被连接的 sp2
      }
    }

    return this;
  }

  , reverse: function () {
    if (this.is('ellipse')) {
      var result = new Path();
      var args = this.subPaths[0].actions[1].args;

      result.ellipse(
        args[0],
        args[1],
        args[2],
        args[3],
        args[5], // end as start
        args[4], // start as end
        !args[6] // invert ccw
      );

      return result;
    }

    var result = new Path();

    result.subPaths = this.subPaths.map(function (sp) {
      return sp.reverse();
    }).reverse();

    return result;
  }

  , sort: function () {
    if (this.subPaths.length === 0) return this;

    var copy = new Path();

    var p0 = this.subPaths[0].lastPoint();

    copy.subPaths = this.subPaths.sort(function (a, b) {
      var p1 = a.lastPoint();
      var p2 = b.firstPoint();

      var d1 = Point.distance(p1, p0);
      var d2 = Point.distance(p2, p0);

      // Moving target
      p0 = b.lastPoint();

      if (d1 < d2) return -1;
      if (d1 > d2) return 1;

      return 0;
    });

    return copy;
  }

  , firstPoint: function () {
    if (!this.current) return false;
    return this.subPaths[0].firstPoint();
  }

  , lastPoint: function () {
    if (!this.current) return false;
    return this.subPaths[this.subPaths.length - 1].lastPoint();
  }

  , getPoints: function (divisions) {
    var pts = [];
    this.subPaths.forEach(function (sp) {
      pts.push.apply(pts, sp.getPoints(divisions));
    });
    return pts;
  }

  // 返回路径的边界矩形
  , getBounds: function () {
    var pts = this.getPoints();
    var p0 = this.firstPoint();
    var res = {
      left: p0.x, // minX
      top: p0.y, // maxY
      right: p0.x,  // maxX
      bottom: p0.y // minY
    };

    pts.forEach(function (p) {
      res.left = Math.min(res.left, p.x);
      res.bottom = Math.min(res.bottom, p.y);
      res.right = Math.max(res.right, p.x);
      res.top = Math.max(res.top, p.y);
    });
    // pts.forEach(function(p) {
    //   res.left = Math.min(res.left, p.x);
    //   res.top = Math.min(res.top, p.y);
    //   res.right = Math.max(res.right, p.x);
    //   res.bottom = Math.max(res.bottom, p.y);
    // });
    return res;
  }
  /**
   * @param diameter 填充的线间距
   * @param divisions 填充的分段数
   * @param fillDir 填充方向
   */
  , fillPath: function (diameter, divisions, fillDir) {
    let bounds = this.getBounds();
    let w = (bounds.right - bounds.left) * RESOLUTION;
    let h = (bounds.top - bounds.bottom) * RESOLUTION;
    let step = diameter * RESOLUTION;
    let x0 = bounds.left * RESOLUTION;
    let y0 = bounds.bottom * RESOLUTION;
    if (fillDir === conf.FillDirection.ContourParallel) {
      return this.fillPathOffset(diameter, divisions);
    } else {
      return this.buildGridFilling(w, h, step, fillDir, divisions, x0, y0);
    }
  }

  , buildGridFilling: function (w, h, step, fillDir, divisions, x0 = 0, y0 = 0) {
    var path = new Path();
    let c = new ClipperLib.Clipper();

    // this.addGridClip(plist, c);
    let subjPolys = this.toPolys(RESOLUTION, divisions);
    c.AddPaths(subjPolys, ClipperLib.PolyType.ptClip, true);
    // console.log(c.Area(subjPolys[0]));
    let zags;
    if (fillDir === conf.FillDirection.ZigZag) {
      // this.turnPoints = c.findTurnPoints();
      // c.AddPaths(subjPolys, ClipperLib.PolyType.ptSubject, false);
      
      zags = this.getZagClip( subjPolys, w, h, step, x0, y0);
    }
    // 添加网格 
    this.addGridSubject(c, w, h, fillDir, step, x0, y0);

    let solution = new ClipperLib.PolyTree();
    // var clipped = [];
    let succeeded = c.Execute(ClipperLib.ClipType.ctIntersection, solution, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd);

    if (!succeeded)
      return path;

    let ll = ClipperLib.Clipper.OpenPathsFromPolyTree(solution); // ll : ClipperLib.Paths()
    // return ToPotraceList(ll, false);

    // path.fromPolys(clipped, RESOLUTION);
    if (fillDir === conf.FillDirection.ZigZag){
      ll =  path.mergeZigZag(ll, zags, step, RESOLUTION, false);
    } 

    path.fromPolys(ll, RESOLUTION, false);

    return path;
  }
  // step: 填充的线间距
  , addGridSubject: function (c, w, h, dir, step, x0 = 0, y0 = 0) {
    let paths = [];
    let dstep = step * Math.sqrt(2); // 对角线步进值 （1.414）
    let rdstep = step * (1 / Math.sqrt(2));
    // 90 度特殊处理
    let yAngleStep = 0;
    let xAngleStep = 0;
    // if (fillAngle >= 89.9 && fillAngle <= 90.1) {
    //   // TODO： 如果水平单向则转为垂直单向
    // } else if (fillAngle >= 269.9 && fillAngle <= 270.1) {

    // }else if (fillAngle > 0 && fillAngle < 0.1 || (fillAngle > 359.9 && fillAngle < 360.1)) {

    // }else if (fillAngle > 179.9 && fillAngle < 180.1) {

    // } else {
    //   yAngleStep = step / Math.cos(Math.PI * fillAngle / 180); 
    //   xAngleStep = step / Math.sin(Math.PI * fillAngle / 180); 
    // }

    if (dir === conf.FillDirection.HorizontalUni) {
      for (let y = 1; y < (h / step); y++) {
        this.addSegment(paths, x0, y0 + y * step, x0 + w, y0 + y * step, true);
      }
    }

    // if (dir === conf.FillDirection.HorizontalUni && fillAngle !== 0) {
    //   let lineCount =  w / (Math.sin(Math.PI * fillAngle / 180)) + h / (Math.cos(Math.PI * fillAngle / 180));
    //   if ( (fillAngle > 0 && fillAngle < 90) || (fillAngle > 270 && fillAngle < 360)){
    //     for (let i = 1; i < lineCount; i++) {
    //       this.addSegment(paths, x0, y0 + h - i * yAngleStep, x0 + i * xAngleStep, y0 + h, fillAngle > 180);
    //     }
    //   } else if( fillAngle > 90 && fillAngle < 180){
    //     for (let i = 1; i < lineCount; i++) {
    //       this.addSegment(paths, x0, y0 + i * yAngleStep, x0 + i * xAngleStep, y0, fillAngle > 180);
    //     }
    //   } else {
    //     console.log("暂不支持该填充方向");
    //   }
    // }

    if (dir === conf.FillDirection.HorizontalBi || dir === conf.FillDirection.Grid || dir === conf.FillDirection.ZigZag) {
      for (let y = 1; y < (h / step); y++) {
        this.addSegment(paths, x0, y0 + y * step, x0 + w, y0 + y * step, y % 2 === 1);
      }
    }
    if (dir === conf.FillDirection.VerticalUni) {
      for (let x = 1; x < (w / step); x++)
        this.addSegment(paths, x0 + x * step, y0, x0 + x * step, y0 + h, false);
    }
    if (dir === conf.FillDirection.VerticalBi || dir === conf.FillDirection.Grid) {
      for (let x = 1; x < (w / step); x++)
        this.addSegment(paths, x0 + x * step, y0, x0 + x * step, y0 + h, x % 2 === 0);
    }
    if (dir === conf.FillDirection.Diagonal || dir === conf.FillDirection.DiagonalGrid) {
      for (let i = 0; i < (w + h) / dstep; i++)
        this.addSegment(paths, x0, y0 + i * dstep, x0 + i * dstep, y0, i % 2 === 0);
    }
    if (dir === conf.FillDirection.ReverseDiagonal || dir === conf.FillDirection.DiagonalGrid) {
      for (let i = 0; i < (w + h) / dstep; i++)
        this.addSegment(paths, x0, y0 + h - (i * dstep), x0 + i * dstep, y0 + h, i % 2 === 1);
    }
    if (dir === conf.FillDirection.Cross) {
      let cl = step / 3; //cross len
      for (let y = 1; y < (h / step); y++) {
        for (let x = 1; x < (w / step); x++) {
          this.addSegment(paths, x0 + (x * step) - cl, y0 + y * step, x0 + (x * step) + cl, y0 + y * step, y % 2 === 1);
          this.addSegment(paths, x0 + x * step, y0 + (y * step) - cl, x0 + x * step, y0 + (y * step) + cl, x % 2 === 0);
        }
      }
    }
    if (dir === conf.FillDirection.DiagonalCross) {
      let cl = rdstep / 3;
      for (let y = 0; y < Math.floor(h / step) + 1; y++) {
        for (let x = 0; x < Math.floor(w / step) + 1; x++) {
          this.addSegment(paths, x0 + (x * step) - cl, y0 + (y * step) - cl, x0 + (x * step) + cl, y0 + (y * step) + cl, x % 2 === 1);
          this.addSegment(paths, x0 + (x * step) + cl, y0 + (y * step) - cl, x0 + (x * step) - cl, y0 + (y * step) + cl, x % 2 === 1);
        }
      }
    }
    if (dir === conf.FillDirection.Squares) {
      let cl = step / 3;
      for (let y = 0; y < Math.floor(h / step) + 1; y++) {
        for (let x = 0; x < Math.floor(w / step) + 1; x++) {
          let list = [];
          this.addPathPoint(list, x0 + (x * step) - cl, y0 + y * step);
          this.addPathPoint(list, x0 + x * step, y0 + (y * step) + cl);
          this.addPathPoint(list, x0 + (x * step) + cl, y0 + y * step);
          this.addPathPoint(list, x0 + x * step, y0 + (y * step) - cl);
          this.addPathPoint(list, x0 + (x * step) - cl, y0 + y * step);
          this.addPathPoints(paths, list);
        }
      }
    }
    // if (dir === conf.FillDirection.ZigZag) {
    //   let hs = dstep / 2; // halfstep

    //   for (let i = 1; i < (w + h) / dstep; i++) {
    //     let list = [];

    //     if (i % 2 === 1) {
    //       let my = y0;
    //       let mx = x0 + (i * dstep) - hs;

    //       this.addPathPoint(list, mx, my);
    //       while (mx > 0) {
    //         this.addPathPoint(list, mx, my += hs);
    //         this.addPathPoint(list, mx -= hs, my);
    //       }
    //     } else {
    //       let mx = x0;
    //       let my = y0 + (i * dstep) - hs;

    //       this.addPathPoint(list, mx - 1, my + 1); // ensure a slight offset as in the original code comment
    //       this.addPathPoint(list, mx, my);
    //       while (my > 0) {
    //         this.addPathPoint(list, mx += hs, my);
    //         this.addPathPoint(list, mx, my -= hs);
    //       }
    //     }

    //     this.addPathPoints(paths, list);
    //   }
    // }
    if (dir === conf.FillDirection.Hilbert) {
      let n = 5;
      // let n = 6;
      let ts = Math.pow(2, n) * step;
      let texel = Hilbert.Execute(n, step, x0 + step, y0 + step);

      for (let y = 0; y < Math.ceil(h / ts); y++) {
        for (let x = 0; x < Math.ceil(w / ts); x++) {
          let list = [];
          for (let i = 0; i < texel.length; i++) {
            this.addPathPoint(list, texel[i].X + (x * ts), texel[i].Y + (y * ts));
          }
          this.addPathPoints(paths, list);
        }
      }
    }

    c.AddPaths(paths, ClipperLib.PolyType.ptSubject, false);
  }

  , getZagClip: function (subjPolys, w, h, step, x0 = 0, y0 = 0) {
    let zagL;

    let czag = new ClipperLib.Clipper();
    czag.AddPaths(subjPolys, ClipperLib.PolyType.ptSubject, false);
    for (let y = 1; y < (h / step); y++) {
      // this.addSegment(paths, x0, y0 + y * step, x0 + w, y0 + y * step, y % 2 === 1);
      let path = [];
      
      this.addPathPoints(path,[
        {X: Math.round(x0), Y: Math.round(y0 + y * step)},
        {X: Math.round(x0 + w), Y: Math.round(y0 + y * step)},
        {X: Math.round(x0 + w), Y: Math.round(y0 + (y + 1) * step)},
        {X: Math.round(x0), Y: Math.round(y0 + (y + 1) * step)},
        {X: Math.round(x0), Y: Math.round(y0 + y * step)}]); // 添加裁剪矩形
      czag.AddPaths(path, ClipperLib.PolyType.ptClip, true);
      
    }
    let solution = new ClipperLib.PolyTree();
    let succeeded = czag.Execute(ClipperLib.ClipType.ctIntersection, solution, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd);
    if(succeeded){
      // zagL.push(solution);
      zagL = ClipperLib.Clipper.OpenPathsFromPolyTree(solution);
    } 
    
    return zagL;
  }

  , rotateMode: function(yAxisStepLength){
    var path = new Path();

    let bounds = this.getBounds();
    let w = (bounds.right - bounds.left) * RESOLUTION;
    let h = (bounds.top - bounds.bottom) * RESOLUTION;
    let step = yAxisStepLength * RESOLUTION;
    let x0 = bounds.left * RESOLUTION;
    let y0 = bounds.bottom * RESOLUTION;

    let subjPolys = this.toPolys(RESOLUTION, 20);
    let zags = this.getZagClip( subjPolys, w, h, step, x0, y0);
    path.fromPolys(zags, RESOLUTION, false);

    return path;
  }

  , addSegment: function (paths, x1, y1, x2, y2, reverse) {
    if (reverse) {
      let path = [{ X: Math.round(x2), Y: Math.round(y2) }, { X: Math.round(x1), Y: Math.round(y1) }];
      paths.push(path);
    } else {
      let path = [{ X: Math.round(x1), Y: Math.round(y1) }, { X: Math.round(x2), Y: Math.round(y2) }];
      paths.push(path);
    }
  }

  , addPathPoint: function (target, x, y) {
    target.push({ X: x, Y: y });
  }

  , addPathPoints: function (target, points) {
    target.push(points);
  }

  , mergeZigZag: function ( zigs, zags, step ,scale, isClosed = true) {
    if (!scale) throw 'NO SCALE!';
    this.subPaths = [];

    // 将 zigs 和 zags 合并
    // 默认 zigs 的 Y 是降序排列的，
    let zigzags = [];
    let zigzagsLen = 0;

    let scanY = zigs[0][0].Y; // 扫描线 Y 坐标

    let zagsIndex = 0; // zags 索引
    let thisZigScanYNum = 0; // 当前 zags 在这一层扫描线上的数量

    for (let i = 0; i < zigs.length ; i += thisZigScanYNum) {
      if (zigs[i] === null) continue;
      scanY = zigs[i][0].Y; // 扫描线 Y 坐标

      // 1. 确定 zigs 和 zags 在这一层扫描线上的数量
      thisZigScanYNum = 0; 
      for(let ii = 0 ; ii < zigs.length - i ; ++ii ){
        if(Math.abs(zigs[i+ii][0].Y - scanY) < 1){
          thisZigScanYNum++;
        }else{
          break;
        }
        // if(Math.abs(zigs[i+ii][0].Y - scanY) > 1) break;
      } 

      let thisZagScanYNum = 0; // 当前 zags 在这一层扫描线上的数量
      for(let j = zagsIndex ; j < zags.length; ++j ){
        // 先去除不太对的 zags
        if(Math.abs(zags[j][0].Y - zags[j].at(-1).Y) != step){
          zags[j] = null;
          thisZagScanYNum++;
          zagsIndex++;
          continue;
        }
        if(Math.abs(zags[j][0].Y - scanY) < 1 ){
          thisZagScanYNum++;
          zagsIndex++;
        }else if( Math.abs(zags[j].at(-1).Y - scanY) < 1 ){
          zags[j] = zags[j].reverse();
          thisZagScanYNum++;
          zagsIndex++;
        } else {
          break;
        }
        // else (Math.abs(zags[j][0].Y - scanY) > 1 && 
        //    Math.abs(zags[j].at(-1).Y - scanY) > 1) 
        //    break;
      }

      // 2. 先看 zigs 有没有可以 merge 到路进中的
      // 原则： 可以合并则合并，不能合并的路径新开一条路径
      if (zigzagsLen === 0) { // 第一条zigzags
        for(let k = 0; k < thisZigScanYNum; ++k){
          zigzags.push(zigs[i+k]);
          zigzagsLen++;
        }
      } else {
        for(let k = 0; k < thisZigScanYNum; ++k){
          let isMerge = false;
          for(let kk = 0; kk < zigzagsLen; ++kk){
            if(this.isSameClipperPoint(zigs[i+k][0], zigzags[kk].at(-1))){ // zigs 的头和 zigzags 的尾是同一个点
              // zigzags[kk] = zigzags[kk].concat(zigs[i+k]);
              zigzags[kk].push(...zigs[i+k].splice(1));
              isMerge = true;
              break;
            }
          }
          if(!isMerge){
            zigzags.push(zigs[i+k]);
            zigzagsLen++;
          }
        }
      }

      // 3. 再看 zags 有没有可以 merge 到路进中的。
      // 原则： 可以合并则合并，不能合并的路径舍弃
      for(let k = zagsIndex - 1; k > zagsIndex - thisZagScanYNum - 1; k--){
        if(zags[k] === null) continue;
        for(let kk = 0; kk < zigzagsLen; ++kk){
          if(this.isSameClipperPoint(zags[k][0], zigzags[kk].at(-1))){ // zags 的头和 zigzags 的尾是同一个点
            // zigzags[kk] = zigzags[kk].concat(zags[k]);
            zigzags[kk].push(...zags[k].splice(1));
            // isMerge = true;
            break;
          }
        }
      }
    }
    return zigzags;
  }

  , isSameClipperPoint: function (p1, p2) {
    return ((Math.abs(p1.X - p2.X) < 1) && (Math.abs(p1.Y - p2.Y) < 1));
  }

  , isSameInt: function (a, b) {
    return Math.abs(a - b) < 1;
  }

}


const Hilbert = {
  // 曲线深度、每一步的位移距离
  Execute: function (depth, delta, x0, y0) {
    let rv = [];
    let dy = 0;
    let dx = delta;
    rv.push({ X: x0, Y: y0 }); // Add starting point
    this.doHilbert(rv, depth, dx, dy);
    return rv;
  },

  // 递归生成 Hilbert 曲线的点序列
  doHilbert: function (list, depth, dx, dy) {
    if (depth > 1) this.doHilbert(list, depth - 1, dy, dx);
    list.push({ X: list[list.length - 1].X + dx, Y: list[list.length - 1].Y + dy });
    if (depth > 1) this.doHilbert(list, depth - 1, dx, dy);
    list.push({ X: list[list.length - 1].X + dy, Y: list[list.length - 1].Y + dx });
    if (depth > 1) this.doHilbert(list, depth - 1, dx, dy);
    list.push({ X: list[list.length - 1].X - dx, Y: list[list.length - 1].Y - dy });
    if (depth > 1) this.doHilbert(list, depth - 1, -dy, -dx);
  }
};


var NON_ZERO = ClipperLib.PolyFillType.pftNonZero;
var EVEN_ODD = ClipperLib.PolyFillType.pftEvenOdd;













// 合并给定的路径数组 paths
// 并确保合并后的路径不会越过边界 bounds
// CamPath 的格式: {
//      path:        Clipper path
//      safeToClose: Is it safe to close the path without retracting?
// }
// , mergerPaths: function (bounds, paths) {
//   if (paths.length === 0) { return []; } // 没有路径

//   let currentPath = paths[0]; // 第一个路径作为当前路径
//   if (pathIsClosed(currentPath)) { // 闭合路径，先将点添加到 paths 中
//     currentPath.push(currentPath[0]);
//   }

//   let currentPoint = currentPath[currentPath.length - 1];
//   paths[0] = []; // 清空第一个点

//   let mergedPaths = []; // 存储合并后的路径
//   let numLeft = paths.length - 1; // 记录剩余未处理的路径数量
//   while (numLeft > 0) {
//     let closestPathIndex = null;
//     let closestPointIndex = null;
//     let closestPointDist = null;
//     let closestReverse = false;
//     for (let pathIndex = 0; pathIndex < paths.length; ++pathIndex) {
//       let path = paths[pathIndex];
//       // 检查当前路径上的点与当前点的距离
//       function check(pointIndex) {
//         let point = path[pointIndex];
//         let dist = (currentPoint.X - point.X) * (currentPoint.X - point.X) + (currentPoint.Y - point.Y) * (currentPoint.Y - point.Y);
//         if (closestPointDist === null || dist < closestPointDist) {
//           closestPathIndex = pathIndex;
//           closestPointIndex = pointIndex;
//           closestPointDist = dist;
//           closestReverse = false;
//           return true;
//         }
//         else
//           return false;
//       }

//       if (pathIsClosed(path)) {
//         for (let pointIndex = 0; pointIndex < path.length; ++pointIndex)
//           check(pointIndex);
//       } else if (path.length) {
//         check(0);
//         if (check(path.length - 1))
//           closestReverse = true;
//       }
//     }

//     let path = paths[closestPathIndex];
//     paths[closestPathIndex] = [];
//     numLeft -= 1;
//     let needNew;
//     if (pathIsClosed(path)) {
//       needNew = crosses(bounds, currentPoint, path[closestPointIndex]);
//       path = path.slice(closestPointIndex, path.length).concat(path.slice(1, closestPointIndex));
//       path.push(path[0]);
//     } else {
//       needNew = true;
//       if (closestReverse) {
//         path = path.slice();
//         path.reverse();
//       }
//     }
//     if (needNew) {
//       mergedPaths.push(currentPath);
//       currentPath = path;
//       currentPoint = currentPath[currentPath.length - 1];
//     }
//     else {
//       currentPath = currentPath.concat(path);
//       currentPoint = currentPath[currentPath.length - 1];
//     }
//   }
//   mergedPaths.push(currentPath);

//   let camPaths = [];
//   for (let i = 0; i < mergedPaths.length; ++i) {
//     let path = mergedPaths[i];
//     camPaths.push({
//       path: path,
//       safeToClose: !crosses(bounds, path[0], path[path.length - 1])
//     });
//   }

//   return camPaths;
// }

// , clipperBounds: function (paths) {
//   let minX = Number.MAX_VALUE;
//   let minY = Number.MAX_VALUE;
//   let maxX = -Number.MAX_VALUE;
//   let maxY = -Number.MAX_VALUE;
//   for (let path of paths) {
//     for (let pt of path) {
//       minX = Math.min(minX, pt.X);
//       maxX = Math.max(maxX, pt.X);
//       minY = Math.min(minY, pt.Y);
//       maxY = Math.max(maxY, pt.Y);
//     }
//   }
//   return { minX, minY, maxX, maxY };
// }
// geometry：Clipper 几何结构，可能是一个或多个路径组成的数组
// lineDistance： 扫描线之间的距离，用于控制填充的密度
// angle: 填充路径的旋转角度
// , fillPath2: function (geometry, lineDistance, angle) {
//   if (!geometry.length || !geometry[0].length)
//     return [];
//   let bounds = clipperBounds(geometry); // 计算几何结构的边界矩形
//   let cx = (bounds.minX + bounds.maxX) / 2;
//   let cy = (bounds.minY + bounds.maxY) / 2;
//   let r = utils.dist(cx, cy, bounds.minX, bounds.minY) + lineDistance;

//   // TODO: 调用 matrix 解决坐标问题
//   let m = mat3.fromTranslation([], [cx, cy]);
//   m = mat3.rotate([], m, angle * Math.PI / 180);
//   m = mat3.translate([], m, [-cx, -cy]);
//   let makePoint = (x, y) => {
//     let p = vec2.transformMat3([], [x, y], m);
//     return { X: p[0], Y: p[1] };
//   }

//   let scan = [];
//   for (let y = cy - r; y < cy + r; y += lineDistance * 2) {
//     scan.push(
//       makePoint(cx - r, y),
//       makePoint(cx + r, y),
//       makePoint(cx + r, y + lineDistance),
//       makePoint(cx - r, y + lineDistance),
//     );
//   }

//   let allPaths = [];
//   let separated = separateTabs(scan, geometry);
//   for (let i = 1; i < separated.length; i += 2)
//     allPaths.push(separated[i]);
//   return mergePaths(null, allPaths);
// }


